package ru.romannep.pc.chopperbattle;

import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.imageio.ImageIO;
import javax.swing.*;

/**
 * Класс реализующий визуальное отображение игры. Представляет собой JFrame
 * фиксированных размеров с двумя JPanel в CardLayout: первая - отображает лишь
 * титульную страницу, во второй рисуется сам игровой процесс и обрабатываются
 * команды клавиатуры
 *
 */
public class PCGame extends JFrame {

    private static final int windowWidth = 640;
    private static final int windowHeigth = 396;
    public static final String TITLE_TAB = "title";
    public static final String GAME_TAB = "game";
    public static final String NET_TAB = "network";
    public static final String START_LEVEL = "/ru/romannep/pc/chopperbattle/resources/level1";
    public CardLayout layout;
    public JPanel mainPanel;
    public GamePanel gamePanel;
    public NetworkPanel netPanel;
    int serverPort = 3344;
    String serverAddress = "localhost";
    ChopperBattleConnectionToServer connection;

    /**
     * Панель отображения игрового процесса
     */
    class GamePanel extends JPanel {

        private Timer timer;
        private ChopperBattleGameEngine ge2;
        private BufferedImage field;
        private BufferedImage fieldTitle;
        private BufferedImage title;
        private BufferedImage[] chop1img = new BufferedImage[12];
        private BufferedImage[] chop2img = new BufferedImage[12];
        private BufferedImage[] destroyimg = new BufferedImage[6];
        private int chop1imgIndex = 0;
        private int chop2imgIndex = 0;
        private static final float FIELD_POS_X = 0;
        private static final float FIELD_POS_Y = 16;
        private static final float FIELD_TITLE_POS_X = 0;
        private static final float FIELD_TITLE_POS_Y = 0;
        private static final int FUEL_1_POS_X = 137;
        private static final int FUEL_2_POS_X = 523;
        private static final int FUEL_SIZE_X = 67;
        private static final int FUEL_SIZE_Y = 9;
        private static final int FUEL_POS_Y = 3;
        private static final int DROPS_POS_Y = 13;
        private static final int DROPS_1_POS_X = 210;
        private static final int DROPS_2_POS_X = 598;
        private final Rectangle2D winnerRect = new Rectangle2D.Float(200, 150, 200, 100);
        private static final int WINNER_TEXT_X = 250;
        private static final int WINNER_TEXT_Y = 200;
        private static final int WINTEXTCICLES = 50; //количество циклов таймера для отображения победителя при окончании игры
        private int wintextcycles = WINTEXTCICLES;

        public GamePanel() {

            //загрузка спрайтов
            try {
                field = ImageIO.read(this.getClass().getResource("/ru/romannep/pc/chopperbattle/resources/field1.png"));
                fieldTitle = ImageIO.read(this.getClass().getResource("/ru/romannep/pc/chopperbattle/resources/field_title.png"));

                title = ImageIO.read(this.getClass().getResource("/ru/romannep/pc/chopperbattle/resources/title.png"));
                for (int i = 0; i < 6; i++) {
                    chop1img[i] = ImageIO.read(this.getClass().getResource("/ru/romannep/pc/chopperbattle/resources/chop1_" + i + ".png"));
                }
                //спрайтов всего 6, но чтоб пропеллер крутился туда-обратно - добавляем ссылками еще 6
                for (int i = 0; i < 6; i++) {
                    chop1img[6 + i] = chop1img[5 - i];
                }
                for (int i = 0; i < 6; i++) {
                    chop2img[i] = ImageIO.read(this.getClass().getResource("/ru/romannep/pc/chopperbattle/resources/chop2_" + i + ".png"));
                }
                for (int i = 0; i < 6; i++) {
                    chop2img[6 + i] = chop2img[5 - i];
                }
                for (int i = 0; i < 6; i++) {
                    destroyimg[5 - i] = ImageIO.read(this.getClass().getResource("/ru/romannep/pc/chopperbattle/resources/destroy_" + i + ".png"));
                }
            } catch (IOException ex) {
            }


            //установка таймера игрового процесса
            timer = new Timer(1000 / ChopperBattleGameEngine.GAME_CYCLES_PER_SECOND, new ActionListener() {

                @Override
                public void actionPerformed(ActionEvent e) {

                    if (ge2.gameRunning) {

                        //двигаем кадры анимации вертолетов
                        //если у вертолета двигатель запущен, то анимация идет быстрей
                        chop1imgIndex++;
                        if (ge2.chopper1.engineOn()) {
                            chop1imgIndex++;
                        }
                        chop1imgIndex = chop1imgIndex > 11 ? chop1imgIndex - 12 : chop1imgIndex;
                        chop2imgIndex++;
                        if (ge2.chopper2.engineOn()) {
                            chop2imgIndex++;
                        }
                        chop2imgIndex = chop2imgIndex > 11 ? chop2imgIndex - 12 : chop2imgIndex;

                        //вызываем выполнение игрового цикла
                        ge2.gameCycle();

                        repaint();
                        //requestFocus();
                    } else {
                        wintextcycles++;
                    }

                    //обработка счетчика времени показа победителя
                    if (wintextcycles > WINTEXTCICLES) {
                        timer.stop();
                        layout.show(mainPanel, TITLE_TAB);
                    }
                }
            });

            addKeyListener(new KeyAdapter() {

                @Override
                public void keyPressed(KeyEvent k) {
                    GameChopperCommand chopper1command = GameChopperCommand.NULL;
                    GameChopperCommand chopper2command = GameChopperCommand.NULL;
                    switch (k.getKeyCode()) {
//                        case KeyEvent.VK_F1:
//                            if (!ge2.gameRunning) {
//                            }
//                            break;
//                        case KeyEvent.VK_ESCAPE:
//                            stopGame();
//                            break;
//                        case KeyEvent.VK_PAUSE:
//                            pauseGame();
//                            break;
                        case KeyEvent.VK_W:
                            chopper1command = GameChopperCommand.UP_ON;
                            break;
                        case KeyEvent.VK_D:
                            chopper1command = GameChopperCommand.RIGHT_ON;
                            break;
                        case KeyEvent.VK_A:
                            chopper1command = GameChopperCommand.LEFT_ON;
                            break;
                        case KeyEvent.VK_E:
                            chopper1command = GameChopperCommand.FIRE;
                            break;
                        case KeyEvent.VK_UP:
                            chopper2command = GameChopperCommand.UP_ON;
                            break;
                        case KeyEvent.VK_RIGHT:
                            chopper2command = GameChopperCommand.RIGHT_ON;
                            break;
                        case KeyEvent.VK_LEFT:
                            chopper2command = GameChopperCommand.LEFT_ON;
                            break;
                        case KeyEvent.VK_CONTROL:
                            chopper2command = GameChopperCommand.FIRE;
                            break;
                    }
                    if (chopper1command != GameChopperCommand.NULL) {
                        ge2.chopperCommand(ge2.chopper1, chopper1command);
                    }
                    if (chopper2command != GameChopperCommand.NULL) {
                        ge2.chopperCommand(ge2.chopper2, chopper2command);
                    }
                }

                @Override
                public void keyReleased(KeyEvent k) {
                    GameChopperCommand chopper1command = GameChopperCommand.NULL;
                    GameChopperCommand chopper2command = GameChopperCommand.NULL;

                    switch (k.getKeyCode()) {
                        case KeyEvent.VK_W:
                            chopper1command = (GameChopperCommand.UP_OFF);
                            break;
                        case KeyEvent.VK_D:
                            chopper1command = (GameChopperCommand.RIGHT_OFF);
                            break;
                        case KeyEvent.VK_A:
                            chopper1command = (GameChopperCommand.LEFT_OFF);
                            break;
                        case KeyEvent.VK_UP:
                            chopper2command = GameChopperCommand.UP_OFF;
                            break;
                        case KeyEvent.VK_RIGHT:
                            chopper2command = GameChopperCommand.RIGHT_OFF;
                            break;
                        case KeyEvent.VK_LEFT:
                            chopper2command = GameChopperCommand.LEFT_OFF;
                            break;

                    }
                    if (chopper1command != GameChopperCommand.NULL) {
                        ge2.chopperCommand(ge2.chopper1, chopper1command);
                    }
                    if (chopper2command != GameChopperCommand.NULL) {
                        ge2.chopperCommand(ge2.chopper2, chopper2command);
                    }

                }
            });

            setFocusable(true);
            requestFocus();

        }

        @Override
        public void paintComponent(Graphics g) {
            //super.paintComponent(g);
            //создаем буфер и рисуем на нем
            BufferedImage buffer = new BufferedImage(windowWidth, windowHeigth, BufferedImage.TYPE_INT_RGB);
            Graphics2D g2 = (Graphics2D) buffer.getGraphics();

            //игровое поле с шапкой
            g2.drawImage(fieldTitle,
                    (int) (FIELD_TITLE_POS_X), (int) (FIELD_TITLE_POS_Y),
                    (int) (FIELD_POS_X + ge2.FIELD_SIZE_X), (int) (FIELD_POS_Y),
                    0, 0, fieldTitle.getWidth(), fieldTitle.getHeight(),
                    null);

            g2.drawImage(field,
                    (int) (FIELD_POS_X), (int) (FIELD_POS_Y),
                    (int) (FIELD_POS_X + ge2.FIELD_SIZE_X), (int) (FIELD_POS_Y + ge2.FIELD_SIZE_Y),
                    0, 0, field.getWidth(), field.getHeight(),
                    null);
//можно раскоментировать, чтобы увидеть границы
//        for (GameElement obj : ge2.borders){
//            g2.draw(new Rectangle2D.Float(FIELD_POS_X+obj.x,FIELD_POS_Y+obj.y,obj.sx,obj.sy));
//        }

            g2.setColor(new Color(0, 0, 0));
            //отрисовка вертолетов, которые в игре, их топлива, игрового счета
            if (ge2.chopper1.inGame) {
                g2.fill(new Rectangle2D.Float(FUEL_1_POS_X - (ge2.FUEL_CAPACITY - ge2.chopper1.fuel) / ge2.FUEL_CAPACITY * FUEL_SIZE_X, FUEL_POS_Y, (ge2.FUEL_CAPACITY - ge2.chopper1.fuel) / ge2.FUEL_CAPACITY * FUEL_SIZE_X, FUEL_SIZE_Y));
                g2.drawImage(chop1img[chop1imgIndex],
                        (int) (FIELD_POS_X + ge2.chopper1.x + (ge2.chopper1.directionRight ? 0 : ge2.chopper1.sx)), (int) (FIELD_POS_Y + ge2.chopper1.y),
                        (int) (FIELD_POS_X + ge2.chopper1.x + (ge2.chopper1.directionRight ? ge2.chopper1.sx : 0)), (int) (FIELD_POS_Y + ge2.chopper1.y + ge2.chopper1.sy),
                        0, 0, chop1img[0].getWidth(), chop1img[0].getHeight(),
                        null);

            }
            if (ge2.chopper2.inGame) {
                g2.drawImage(chop2img[chop2imgIndex],
                        (int) (FIELD_POS_X + ge2.chopper2.x + (ge2.chopper2.directionRight ? 0 : ge2.chopper2.sx)), (int) (FIELD_POS_Y + ge2.chopper2.y),
                        (int) (FIELD_POS_X + ge2.chopper2.x + (ge2.chopper2.directionRight ? ge2.chopper2.sx : 0)), (int) (FIELD_POS_Y + ge2.chopper2.y + ge2.chopper2.sy),
                        0, 0, chop2img[0].getWidth(), chop1img[0].getHeight(),
                        null);
                g2.fill(new Rectangle2D.Float(FUEL_2_POS_X - (ge2.FUEL_CAPACITY - ge2.chopper2.fuel) / ge2.FUEL_CAPACITY * FUEL_SIZE_X, FUEL_POS_Y, (ge2.FUEL_CAPACITY - ge2.chopper2.fuel) / ge2.FUEL_CAPACITY * FUEL_SIZE_X, FUEL_SIZE_Y));
            }
            g2.drawString("" + ge2.chopper1drops + "(" + ge2.MAX_DROPS + ")", DROPS_1_POS_X, DROPS_POS_Y);
            g2.drawString("" + ge2.chopper2drops + "(" + ge2.MAX_DROPS + ")", DROPS_2_POS_X, DROPS_POS_Y);
            //отрисовка пуль
            g2.setColor(new Color(255, 255, 255));
            for (GameElement obj : ge2.bullets) {
                g2.draw(new Rectangle2D.Float(FIELD_POS_X + obj.x, FIELD_POS_Y + obj.y, obj.sx, obj.sy));
            }
            //отрисовка анимации взрыва вертолета
            for (DestroyAnim da : ge2.destroy) {
                g2.drawImage(destroyimg[da.count],
                        (int) (FIELD_POS_X + da.x), (int) (FIELD_POS_Y + da.y),
                        null);
            }

            //отрисовка сообщения о победителе
            if (ge2.winnerText != "") {
                g2.fill(winnerRect);
                g2.setColor(new Color(0, 0, 0));
                g2.drawString(ge2.winnerText, WINNER_TEXT_X, WINNER_TEXT_Y);
            }

            //выводим буфер
            Graphics2D panelG2 = (Graphics2D) g;
            panelG2.drawImage(buffer, 0, 0, null);
        }

        void startGame(Boolean networkGame) {
            if (networkGame) {
                ge2 = new ChopperBattleGameEngine(connection);
            } else {
                ge2 = new ChopperBattleGameEngine();
            }
            ge2.loadLevel(START_LEVEL);
            
            
            ge2.gameStart();
            wintextcycles = 0;
            timer.start();
        }

        void stopGame() {
            timer.stop();
            layout.show(mainPanel, TITLE_TAB);
        }

        void pauseGame() {
            if (ge2.gameRunning) {
                if (timer.isRunning()) {
                    timer.stop();
                } else {
                    timer.start();
                }
            }
        }
    }

    /**
     * Панель отображения титульной страницы
     */
    class TitlePanel extends JPanel {

        BufferedImage title;

        public TitlePanel() {
            try {
                title = ImageIO.read(this.getClass().getResource("/ru/romannep/pc/chopperbattle/resources/title.png"));
            } catch (IOException ex) {
            }
        }

        @Override
        public void paintComponent(Graphics g) {
            Graphics2D g2 = (Graphics2D) g;
            g2.drawImage(title, 0, 0, null);
        }
    }

    class NetworkPanel extends JPanel implements InterfacePanel {

        JTextArea messageArea;
        JTextField commandField;
        String playerName = "Player 2";

        public NetworkPanel() {
            messageArea = new JTextArea();            
            messageArea.setEditable(false);
            messageArea.setLineWrap(true);
            JScrollPane messageScrollPane = new JScrollPane(messageArea);
            messageScrollPane.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
            commandField = new JTextField();
            commandField.addKeyListener(new KeyAdapter() {

                @Override
                public void keyPressed(KeyEvent k) {
                    switch (k.getKeyCode()) {
                        case KeyEvent.VK_ENTER:
                            connection.sendChatMessage( commandField.getText() );
                            log("->" + commandField.getText());
                            commandField.setText("");
                            break;
                    }

                }
            });
            JButton connectButton = new JButton("Connect");
            connectButton.addActionListener(new ActionListener() {

                @Override
                public void actionPerformed(ActionEvent e) {

                    startConnection();
                }
            });
            JButton disconnectButton = new JButton("Disconnect");
            JButton sendMessage = new JButton("send");
            JButton joinGame = new JButton("Join");
            JButton startGame = new JButton("Start");
            
            JLabel listGames = new JLabel("GAME LIST");
            
            setLayout(new GridBagLayout());
            
            Insets defaultInsets = new Insets(5, 5, 5, 5);
            
            add(new JLabel("Server:"), new GridBagConstraints(0, 0, 1, 1, 0, 1, GridBagConstraints.CENTER, GridBagConstraints.BOTH, defaultInsets, 0, 0));
            add(new JLabel("Port:"), new GridBagConstraints( 0, 1, 1, 1, 0, 1, GridBagConstraints.CENTER, GridBagConstraints.BOTH, defaultInsets, 0, 0));
            add(new JLabel("Your name:"), new GridBagConstraints( 0, 2, 1, 1, 0, 1, GridBagConstraints.CENTER, GridBagConstraints.BOTH, defaultInsets, 0, 0));
            add(connectButton, new GridBagConstraints( 0, 3, 1, 1, 1, 1, GridBagConstraints.CENTER, GridBagConstraints.BOTH, defaultInsets, 0, 0));
            add(disconnectButton, new GridBagConstraints( 1, 3, 1, 1, 1, 1, GridBagConstraints.CENTER, GridBagConstraints.BOTH, defaultInsets, 0, 0));

            add(listGames, new GridBagConstraints( 0, 4, 2, 1, 0, 500, GridBagConstraints.CENTER, GridBagConstraints.BOTH, defaultInsets, 0, 0));

            add(joinGame, new GridBagConstraints( 0, 5, 1, 1, 0, 1, GridBagConstraints.CENTER, GridBagConstraints.BOTH, defaultInsets, 0, 0));
            add(startGame, new GridBagConstraints( 1, 5, 1, 1, 0, 1, GridBagConstraints.CENTER, GridBagConstraints.BOTH, defaultInsets, 0, 0));
            
            add(messageScrollPane, new GridBagConstraints( 2, 0, 2, 5, 2, 0, GridBagConstraints.CENTER, GridBagConstraints.BOTH, defaultInsets, 0, 0));
            add(commandField, new GridBagConstraints( 2, 5, 1, 1, 5, 0, GridBagConstraints.CENTER, GridBagConstraints.BOTH, defaultInsets, 0, 0));
            add(sendMessage, new GridBagConstraints( 3, 5, 1, 1, 1, 0, GridBagConstraints.CENTER, GridBagConstraints.BOTH, defaultInsets, 0, 0));

        }

        @Override
        public void log(String s) {
            messageArea.append("\n" + s);
            messageArea.setCaretPosition(messageArea.getDocument().getLength());

        }
        
        

        private void startConnection() {
            if (connection == null) {
                connection = new ChopperBattleConnectionToServer(serverAddress, serverPort, this, playerName);
            }
            connection.connect();

        }

        @Override
        public void startGame() {
            gamePanel.startGame(true);
            layout.show(mainPanel, GAME_TAB);
            gamePanel.requestFocus(true);
        }
    }

    public PCGame() {
        super("Chopper duel");

        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setResizable(false);

        layout = new CardLayout();
        mainPanel = new JPanel(layout);
        add(mainPanel);
        TitlePanel titlePanel = new TitlePanel();
        mainPanel.add(TITLE_TAB, titlePanel);

        gamePanel = new GamePanel();
        gamePanel.setPreferredSize(new Dimension(windowWidth, windowHeigth));
        mainPanel.add(GAME_TAB, gamePanel);

        netPanel = new NetworkPanel();
        mainPanel.add(NET_TAB, netPanel);

        JMenuBar menuBar = new JMenuBar();
        JMenu mainMenu = new JMenu("Game");
        menuBar.add(mainMenu);
        JMenuItem itemStartDuel = mainMenu.add("Start new duel");
        itemStartDuel.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_F1, 0));
        itemStartDuel.addActionListener(new ActionListener() {

            @Override
            public void actionPerformed(ActionEvent e) {
                gamePanel.startGame(false);
                layout.show(mainPanel, GAME_TAB);
                gamePanel.requestFocus(true);
            }
        });

        JMenuItem itemStopDuel = mainMenu.add("Stop duel");
        itemStopDuel.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0));
        itemStopDuel.addActionListener(new ActionListener() {

            @Override
            public void actionPerformed(ActionEvent e) {
                gamePanel.stopGame();
            }
        });

        JMenuItem itemPauseDuel = mainMenu.add("Pause duel");
        itemPauseDuel.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_PAUSE, 0));
        itemPauseDuel.addActionListener(new ActionListener() {

            @Override
            public void actionPerformed(ActionEvent e) {
                gamePanel.pauseGame();
            }
        });

        mainMenu.addSeparator();
        JMenuItem itemExit = mainMenu.add("Exit");
        itemExit.addActionListener(new ActionListener() {

            @Override
            public void actionPerformed(ActionEvent e) {
                System.exit(0);
            }
        });


        JMenu stuffMenu = new JMenu("Stuff");
        JMenuItem saveLevel = stuffMenu.add("Save level");
        saveLevel.addActionListener(new ActionListener() {

            @Override
            public void actionPerformed(ActionEvent e) {
                try {
                    gamePanel.ge2.saveLevel("level1");
                } catch (IOException ex) {
                }
            }
        });

        JMenuItem startServer = stuffMenu.add("Start server");
        startServer.addActionListener(new ActionListener() {

            @Override
            public void actionPerformed(ActionEvent e) {
                //ChopperBattleGameServer.getGameServer().startServer();
                new ServerFrame();
            }
        });

        JMenuItem goNetwork = stuffMenu.add("Go network");
        goNetwork.addActionListener(new ActionListener() {

            @Override
            public void actionPerformed(ActionEvent e) {
                layout.show(mainPanel, NET_TAB);
            }
        });

        menuBar.add(stuffMenu);

        setJMenuBar(menuBar);

        pack();
        setVisible(true);
    }

    public static void main(String[] args) {

        new PCGame();

    }
}
